/*
 * Copyright (c) 2017 深圳市高德威技术有限公司  All rights reserved.
 *
 * serial.c
 *
 *  Created on: 2017年3月28日
 *      Author: chenshisheng
 */


#include "serial.h"
#include <assert.h>
#include <chprintf.h>
#include "shell.h"
#include "timer.h"
#include "utils.h"
#include "message_queue.h"
#include <stdarg.h>

typedef struct
{
    bool txStarted;
    USART_TypeDef* uart;
    rbuf_t txRingBuffer;
    uint8_t _txBuf[SERIAL_TX_BUFFER_SIZE];
    Serial_Callback_t onTxBegin;
    Serial_Callback_t onTxComplete;
    Serial_Callback_t onRx;
}Serial_Ctrl_t;

typedef struct
{
    USART_TypeDef *uart;
}Serial_Config_t;


static const Serial_Config_t const _configs[Serial_Max + 1] =
{
        {USART2},
        {USART3},
};

static Serial_Ctrl_t _ctrls[Serial_Max + 1];
static IoStream_t _streams[Serial_Max + 1];
static uint8_t _bufs[Serial_Max + 1][SERIAL_RX_BUFFER_SIZE];

static void _DoNothing(void *ptr);
static void _OnSerial2Rx(void *ptr);
static void _OnSerial3Rx(void *ptr);
static unsigned int _Send(void *stream, const void *data, unsigned int size);
static void _Uart1Init(void);
static void _Uart2Init(void);
static void _Uart3Init(void);

void Serial_Init(void)
{
    Serial_t serial;
    Serial_Ctrl_t *ctrl;

    for(serial = 0; serial <= Serial_Max; serial++)
    {
        ctrl = & _ctrls[serial];

        ctrl->txStarted = FALSE;
        ctrl->uart = _configs[serial].uart;
        RingBuffer_Init(& ctrl->txRingBuffer, ctrl->_txBuf, sizeof(ctrl->_txBuf));
        ctrl->onTxBegin = _DoNothing;
        ctrl->onTxComplete = _DoNothing;
        ctrl->onRx = _DoNothing;

        RingBuffer_SetFlags(& _ctrls[serial].txRingBuffer, RINGBUFFER_FLAG_OVERWRITE);
        IoStream_Init(& _streams[serial], _bufs[serial], sizeof(_bufs[serial]), _Send,
                (void *) serial);
    }

    _Uart1Init();
    _Uart2Init();
    _Uart3Init();

    Serial_SetCallbacks(Serial_2, NULL, NULL, _OnSerial2Rx);
    Serial_SetCallbacks(Serial_3, NULL, NULL, _OnSerial3Rx);
}

int mprintf(const char *format, ...)
{
    va_list args;
    int ret;

    va_start(args, format);
    ret = chvprintf((Stream_t *) & _streams[STDIO_SERIAL], format, args);
    va_end(args);

    return ret;
}

static void _DoNothing(void *ptr)
{
    (void)ptr;
}

static void _OnSerial2Rx(void *ptr)
{
    char c;
    Shell_LineInMsg_t msg;

    c = *((char *) ptr);

    if(c == '\n')
    {
        msg.stream = (Stream_t *) & _streams[Serial_2];
        msg.till = '\n';
        MsgQueue_Send(MsgQueue_Id_ShellLineIn, & msg, sizeof(msg));
    }
}

static void _OnSerial3Rx(void *ptr)
{
    char c;
    Shell_LineInMsg_t msg;

    c = *((char *) ptr);

    if(c == '\n')
    {
        msg.stream = (Stream_t *) & _streams[Serial_3];
        msg.till = '\n';
        MsgQueue_Send(MsgQueue_Id_ShellLineIn, & msg, sizeof(msg));
    }
}

Stream_t *Serial_ComStream(void)
{
    return (Stream_t * ) & _streams[COM_SERIAL];
}

static unsigned int _Send(void *stream, const void *data, unsigned int size)
{
    IoStream_t *ioStream;
    Serial_t serial;

    ioStream = stream;
    serial = (Serial_t) ioStream->userData;
    Serial_Send(serial, data, size);

    return size;
}

static void _Uart1Init(void)
{

}

static void _Uart2Init(void)
{
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    /* Enable GPIO clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    /* Enable USART clock */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    /* Connect PXx to USARTx_Tx */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);

    /* Connect PXx to USARTx_Rx */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);

    /* Configure USART Tx and Rx as alternate function push-pull */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_DeInit(USART2);
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART2, &USART_InitStructure);

    /* NVIC configuration */
    /* Enable the USARTx Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 0x00;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // Enable the Receive Data register not empty interrupt

    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

    USART_Cmd(USART2, ENABLE);
}

static void _Uart3Init(void)
{
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    /* Enable GPIO clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

    /* Enable USART clock */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);

    /* Connect PXx to USARTx_Tx */
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_4);

    /* Connect PXx to USARTx_Rx */
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_4);

    /* Configure USART Tx and Rx as alternate function push-pull */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

//    USART_DeInit(USART3);
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART3, &USART_InitStructure);

    /* NVIC configuration */
    /* Enable the USARTx Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = USART3_6_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 0x00;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // Enable the Receive Data register not empty interrupt

    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);

    USART_Cmd(USART3, ENABLE);
}

void Serial_SetCallbacks(
        Serial_t serial,
        Serial_Callback_t onTxBegin,
        Serial_Callback_t onTxComplete,
        Serial_Callback_t onRx)
{
    Serial_Ctrl_t *ctrl;

    assert(serial <= Serial_Max);
    ctrl = & _ctrls[serial];

    if(onTxBegin != NULL)
    {
        ctrl->onTxBegin = onTxBegin;
    }

    if(onTxComplete != NULL)
    {
        ctrl->onTxComplete = onTxComplete;
    }

    if(onRx != NULL)
    {
        ctrl->onRx = onRx;
    }
}

void Serial_SendByte(Serial_t serial, uint8_t b)
{
    Serial_Ctrl_t *ctrl;

//    assert(serial <= Serial_Max);

    ctrl = & _ctrls[serial];
    while(RingBuffer_IsFull(& ctrl->txRingBuffer))
    {}

    RingBuffer_Write(& ctrl->txRingBuffer, & b);
    if(! ctrl->txStarted)
    {
        ctrl->onTxBegin(NULL);
        USART_ITConfig(ctrl->uart, USART_IT_TXE, ENABLE);
    }
}

void Serial_Send(Serial_t serial, const void *dataIn, unsigned int size)
{
    const uint8_t *vdata;
    unsigned int i;

    vdata = dataIn;
    for(i = 0; i < size; i++)
    {
        Serial_SendByte(serial, vdata[i]);
    }
}

static void _Isr(Serial_t serial)
{
    Serial_Ctrl_t *ctrl;
    uint8_t b;

//    assert(serial <= Serial_Max);

    ctrl = & _ctrls[serial];
    if (USART_GetITStatus(ctrl->uart, USART_IT_RXNE) == SET)
    {
        b = (uint8_t) USART_ReceiveData(ctrl->uart);
        IoStream_HardwareInput(& _streams[serial], b);
        ctrl->onRx(& b);
    }
    else if(USART_GetITStatus(ctrl->uart, USART_IT_TXE) == SET)
    {
        if(RingBuffer_Read(& ctrl->txRingBuffer, & b) == 0)
        {
            USART_SendData(ctrl->uart, b);
            ctrl->txStarted = TRUE;
        }
        else
        {
            USART_ITConfig(ctrl->uart, USART_IT_TXE, DISABLE);
            USART_ITConfig(ctrl->uart, USART_IT_TC, ENABLE);
            ctrl->txStarted = FALSE;
        }
    }
    else if(USART_GetITStatus(ctrl->uart, USART_IT_TC) == SET)
    {
        USART_ITConfig(ctrl->uart, USART_IT_TC, DISABLE);
        ctrl->onTxComplete(NULL);
    }
}

void Serial_ClearTxBuffer(Serial_t serial)
{
    RingBuffer_Clear(& _ctrls[serial].txRingBuffer);
}

void Serial_Cmd(Serial_t serial, FunctionalState NewState)
{
    USART_Cmd(_ctrls[serial].uart, NewState);
}

#if 0
void USART1_IRQHandler(void)
{
    _Isr(Serial_1);
}
#endif

void USART2_IRQHandler(void)
{
    _Isr(Serial_2);
}

void USART3_6_IRQHandler(void)
{
    _Isr(Serial_3);
}
